package com.longshine.mobile.component.util.soapclient;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;

import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.apache.http.client.methods.HttpGet;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;

import com.longshine.mobile.component.util.HttpConnection;

public class WSDLParser {

	private String elementName;
	private String targetNamespace;
	private Map<Object, Object> messageEntries;
	private Map<Object, Object> portEntries;
	private Map<Object, Object> serviceEntries;

	private MessageEntry lastMessageEntry;
	private PortEntry lastPortEntry;
	private OperationEntry lastOperationEntry;
	private ServiceEntry lastServiceEntry;

	boolean succeed;

	static Map<Object, Object> wsdlParserCache;

	static {
		wsdlParserCache = new HashMap<Object, Object>();
	}

	public WSDLParser() {
		serviceEntries = new HashMap<Object, Object>();
		messageEntries = new HashMap<Object, Object>();
		portEntries = new HashMap<Object, Object>();
	}

	public static WSDLParser loadWithUrl(String wsdlUrl) {
		WSDLParser wsdlParser;
		wsdlParser = (WSDLParser) wsdlParserCache.get(wsdlUrl);
		if (wsdlParser == null) {
			wsdlParser = new WSDLParser();
			wsdlParser.initWithUrl(wsdlUrl);
			if (wsdlParser.succeed) {
				wsdlParserCache.put(wsdlUrl, wsdlParser);
			} else {
				wsdlParser = null;
			}
		}
		return wsdlParser;
	}

	public class MessageEntry {

		String name;
		Object[] parts = null;

		public MessageEntry() {
			parts = new Object[] {};
		}
	}

	public class OperationEntry {
		String name;
		MessageEntry inputMessage;
		MessageEntry outputMessage;
	}

	public class PortEntry {
		String name;
		String serviceLocation;
		Map<Object, Object> operationEntries;

		public PortEntry() {
			operationEntries = new HashMap<Object, Object>();
		}

		public void addOperation(String operationName,
				OperationEntry operationEntry) {
			operationEntries.put(operationName, operationEntry);
		}

		public OperationEntry getOperation(String operationName) {
			return (OperationEntry) operationEntries.get(operationName);
		}
	}

	public class ServiceEntry {
		String name;
		String targetNamespace;
		Map<Object, Object> portEntries;

		public ServiceEntry() {
			portEntries = new HashMap<Object, Object>();
		}

		public void addPort(String portName, PortEntry portEntry) {
			portEntries.put(portName, portEntry);
		}

		public PortEntry getPort(String portName) {
			return (PortEntry) portEntries.get(portName);
		}
	}

	public ServiceEntry getServiceEntry(String serviceName) {
		return (ServiceEntry) serviceEntries.get(serviceName);
	}

	public void initWithUrl(String wsdlUrl) {
		byte[] receivedData = null;
		HttpGet request = new HttpGet(wsdlUrl);

		try {
			receivedData = HttpConnection.httpConnect(request);
		} catch (Exception e) {
			e.printStackTrace();
		}

		if (receivedData != null) {
			InputStream is = new ByteArrayInputStream(receivedData);
			try {
				saxParser(is);
			} catch (Exception e1) {
				e1.printStackTrace();
			}
		}

		if (serviceEntries.size() > 0) {
			succeed = true;
		}
	}

	public void saxParser(InputStream is) throws Exception {
		SAXParserFactory factory = SAXParserFactory.newInstance();
		SAXParser parser = factory.newSAXParser();
		XMLReader xmlReader = parser.getXMLReader();
		SaxParserHandler handler = new SaxParserHandler();
		xmlReader.setContentHandler(handler);
		xmlReader.parse(new InputSource(is));
	}

	public class SaxParserHandler extends DefaultHandler {

		@Override
		public void startElement(String uri, String localName, String qName,
				Attributes attributes) throws SAXException {
			super.startElement(uri, localName, qName, attributes);

			elementName = localName;

			if ("wsdl:definitions".equals(elementName)
					|| "definitions".equals(elementName)) {
				targetNamespace = attributes.getValue("targetNamespace")
						.toString();
			} else if ("wsdl:message".equals(elementName)
					|| "message".equals(elementName)) {
				lastMessageEntry = new MessageEntry();
				lastMessageEntry.name = attributes.getValue("name").toString();
				messageEntries.put(lastMessageEntry.name, lastMessageEntry);
			} else if ("wsdl:part".equals(elementName)
					|| "part".equals(elementName)) {
				String partName = attributes.getValue("name").toString();
				lastMessageEntry.parts = new Object[] { partName };
			} else if ("wsdl:portType".equals(elementName)
					|| "portType".equals(elementName)) {
				lastPortEntry = new PortEntry();
				lastPortEntry.name = attributes.getValue("name").toString();
				portEntries.put(lastPortEntry.name, lastPortEntry);
			} else if ("wsdl:operation".equals(elementName)
					|| "operation".equals(elementName)) {
				if (lastPortEntry != null) {
					lastOperationEntry = new OperationEntry();
					lastOperationEntry.name = attributes.getValue("name")
							.toString();
					lastPortEntry.addOperation(lastOperationEntry.name,
							lastOperationEntry);
				}
			} else if ("wsdl:input".equals(elementName)
					|| "input".equals(elementName)) {
				if (lastOperationEntry != null) {
					String inputName = attributes.getValue("name").toString();
					lastOperationEntry.inputMessage = (MessageEntry) messageEntries
							.get(inputName);
				}
			} else if ("wsdl:output".equals(elementName)
					|| "output".equals(elementName)) {
				if (lastOperationEntry != null) {
					String outputName = attributes.getValue("name").toString();
					lastOperationEntry.outputMessage = (MessageEntry) messageEntries
							.get(outputName);
				}
			} else if ("wsdl:service".equals(elementName)
					|| "service".equals(elementName)) {
				lastServiceEntry = new ServiceEntry();
				lastServiceEntry.name = attributes.getValue("name").toString();
				lastServiceEntry.targetNamespace = targetNamespace;
				serviceEntries.put(lastServiceEntry.name, lastServiceEntry);
			} else if ("wsdl:port".equals(elementName)
					|| "port".equals(elementName)) {
				String portName = attributes.getValue("name").toString();
				lastPortEntry = (PortEntry) portEntries.get(portName);
				lastServiceEntry.addPort(lastPortEntry.name, lastPortEntry);
			} else if ("wsdl:address".equals(elementName)
					|| "address".equals(elementName)) {
				lastPortEntry.serviceLocation = attributes.getValue("location")
						.toString();
			}
		}

		@Override
		public void characters(char[] ch, int start, int length)
				throws SAXException {
			super.characters(ch, start, length);
		}

		@Override
		public void endElement(String uri, String localName, String qName)
				throws SAXException {
			super.endElement(uri, localName, qName);

			elementName = localName;

			if ("wsdl:definitions".equals(elementName)
					|| "definitions".equals(elementName)) {
				lastServiceEntry = null;
				lastMessageEntry = null;
				lastOperationEntry = null;
				lastPortEntry = null;
			} else if ("wsdl:message".equals(elementName)
					|| "messages".equals(elementName)) {
				lastMessageEntry = null;
			} else if ("wsdl:part".equals(elementName)
					|| "part".equals(elementName)) {

			} else if ("wsdl:portType".equals(elementName)
					|| "portType".equals(elementName)) {
				lastPortEntry = null;
			} else if ("wsdl:operation".equals(elementName)
					|| "operation".equals(elementName)) {
				lastOperationEntry = null;
			} else if ("wsdl:input".equals(elementName)
					|| "input".equals(elementName)) {

			} else if ("wsdl:output".equals(elementName)
					|| "output".equals(elementName)) {

			} else if ("wsdl:service".equals(elementName)
					|| "service".equals(elementName)) {
				lastServiceEntry = null;
			} else if ("wsdl:port".equals(elementName)
					|| "port".equals(elementName)) {
				lastPortEntry = null;
			}
		}
	}
}